package cn.matezk.things.broker.service.impl;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.matezk.things.broker.mapper.DeviceMapper;
import cn.matezk.things.broker.mapper.ProductMapper;
import cn.matezk.things.broker.mode.Device;
import cn.matezk.things.broker.mode.Product;
import cn.matezk.things.broker.service.MqttAuthService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.crypto.*;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;

@Service
public class MqttAuthServiceImpl implements MqttAuthService {
    @Autowired
    private ProductMapper productMapper;
    @Autowired
    private DeviceMapper deviceMapper;

    @Override
    public JSONObject register(String productId, String deviceName) throws InvalidAlgorithmParameterException, NoSuchPaddingException, IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException, InvalidKeyException {
        Product product = productMapper.select(productId);
        if (ObjectUtil.isNull(product)) {
            return JSONUtil.createObj().set("code", -1).set("msg", "product is not exists");
        }
        // 查询设备信息
        Device device = deviceMapper.select(productId, deviceName);
        if (ObjectUtil.isNull(device)) {
            device = new Device();
            device.setId(IdUtil.getSnowflake(1, 1).nextId());
            device.setDeviceId(productId.concat(deviceName));
            device.setProductId(productId);
            device.setDeviceName(deviceName);
            device.setDeviceSecret(RandomUtil.randomString(20));
            device.setReported(JSONUtil.createObj());
            deviceMapper.insert(device);
        }
        String psk = encrypt(device.getDeviceSecret(), product.getProductSecret());
        return JSONUtil.createObj()
                .set("code", 0)
                .set("len", psk.length())
                .set("psk", psk);
    }

    @Override
    public boolean auth(String clientId, String userName, String password) throws NoSuchAlgorithmException, InvalidKeyException {
        if (ObjectUtil.equals("provision", userName) && ObjectUtil.isEmpty(password)) return true;
        String items[] = password.split(";");
        if (items.length != 2) return false;
        Long expireTime = Long.valueOf(items[1]);
        if (System.currentTimeMillis() > expireTime * 1000) return false;
        Device device = deviceMapper.select(userName, clientId.substring(userName.length()));
        if (ObjectUtil.isNull(device)) return false;
        Mac hmac = Mac.getInstance("HmacSHA256");
        SecretKeySpec secretKey = new SecretKeySpec(device.getDeviceSecret().getBytes(), "HmacSHA256");
        hmac.init(secretKey);
        String text = clientId.concat(items[1]);
        String token = HexUtil.encodeHexStr(hmac.doFinal(text.getBytes(StandardCharsets.UTF_8)));
        return ObjectUtil.equals(items[1], token);
    }

    private static String encrypt(String plaintext, String productSecret) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidAlgorithmParameterException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
        // 取 productSecret 前16位作为密钥，不足16位时补'0'
        String keyStr = productSecret.length() >= 16 ? productSecret.substring(0, 16) : String.format("%-16s", productSecret).replace(' ', '0');
        byte[] key = keyStr.getBytes(StandardCharsets.UTF_8);

        // 偏移量为16个'0'
        byte[] iv = new byte[16];
        for (int i = 0; i < 16; i++) {
            iv[i] = '0';
        }
        // 创建密钥和偏移量参数
        SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        // 初始化加密器
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivParameterSpec);
        // 加密
        byte[] encryptedBytes = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
        // Base64编码
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }
}
